package com.qs.javame.thread.future;

import java.util.concurrent.*;

/**
 * 测试：单独使用FutureTask。
 * 需要知道的内容：
 *  1) FutureTask间接实现了Runnable、Callable接口，所以可以直接作为executorService.submit(futureTask)执行；
 *  2) futureTask.get()会阻塞线程操作，什么时机该方法会返回？
 *      在使用futureTask.run()方法开始运行时，使用future.cancel(true)，中断运行时，get()方法会返回；
 *      在使用futureTask.run()方法执行完成后，get()方法会返回；
 *
 *  3) 下面的实例可以实现：两个线程执行同一个任务，任务存放在concurrentHashMap中，当一个线程执行到future.get()时会被阻塞，直到两个线程中
 *  的一个线程执行完了futureTask.run()方法后，阻塞在future.get()处的线程会返回计算结果。
 */
public class FutureTaskDemo {

    private final ConcurrentMap<String, Future<String>> taskCache = new ConcurrentHashMap<>();


    private String executionTask(final String taskName) {
        while (true) {
            Future<String> future = taskCache.get(taskName);
            if (future == null) {
                Callable<String> task = new Callable<String>() {
                    @Override
                    public String call() throws Exception {
                        return taskName;
                    }
                };


                FutureTask<String> futureTask = new FutureTask<>(task);
                //如果key存在，则返回key对应的值，如果key不存在，则插入value值
                future = taskCache.putIfAbsent(taskName, futureTask);
                if (future == null) {
                    future = futureTask;

                    //这步骤必不可少，只有futureTask执行了run()方法或者执行cancel()方法，state状态变成已完成(RAN)或已取消(CANCEL)
                    //然后futureTask.get()方法才能返回，否则会一直阻塞。
                    futureTask.run();
                }
            }

            try {
                return future.get();
            } catch (InterruptedException | ExecutionException e) {
                taskCache.remove(taskName, future);
                e.printStackTrace();
            }
        }
    }


    public static void main(String[] args) {
        FutureTaskDemo futureTaskDemo = new FutureTaskDemo();

        Thread thread = new Thread(() -> {
            String task1 = futureTaskDemo.executionTask("task1");
            System.out.println(Thread.currentThread().getName() + ": " + task1);
        });

        thread.start();


        Thread thread2 = new Thread(() -> {
            String task1 = futureTaskDemo.executionTask("task1");
            System.out.println(Thread.currentThread().getName() + ": " + task1);
        });

        thread2.start();
    }

}
